今天我們將實作如何從零開始配置 Redis Client 到 SpringBoot 模組。現在目前使用 JVM 記憶體儲存實現限流功能,接下幾天將整合 Redis 來提升系統的可擴展性和持久性。透過 Redis 的整合,我們可以實現分散式限流,讓多個應用實例共享限流狀態。
首先,我們需要在 pom.xml 中添加 Redis 相關的依賴:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
</dependency>
然後之前我有用 Docker Compose 來啟動我的 MySQL Server,Redis 也可以用同樣的方式做配置,到時就可以直接在容器裡運行 Redis Server,首先設置一下環境變數:
# Redis Configuration
REDIS_HOST=localhost
REDIS_PORT=6379
REDIS_PASSWORD=redis123456
REDIS_DATABASE=0
REDIS_TIMEOUT=2000
REDIS_POOL_MAX_TOTAL=20
REDIS_POOL_MAX_IDLE=10
REDIS_POOL_MIN_IDLE=5
docker-compose.yaml
version: "3.8"
services:
mysql:
# skip...
redis:
image: redis:7-alpine
container_name: core-redis
ports:
- "${REDIS_PORT:-6379}:6379"
volumes:
- redis_data:/data
restart: unless-stopped
command: redis-server --appendonly yes --requirepass ${REDIS_PASSWORD}
volumes:
mysql_data:
redis_data:
實際啟動容器:
docker compose up -d redis
登入看看:
redis-cli -a redis123456
然後 ping 看看可不可以連到 Redis:
127.0.0.1:6379> ping
PONG
大功告成!
application.yaml
再來是 SpringBoot 的配置:
spring:
application:
name: playground-module
datasource:
url: jdbc:h2:mem:testdb
driver-class-name: org.h2.Driver
username: sa
password:
jpa:
hibernate:
ddl-auto: create-drop
show-sql: true
# Redis Configuration
data:
redis:
host: ${REDIS_HOST:localhost}
port: ${REDIS_PORT:6379}
password: ${REDIS_PASSWORD:}
database: ${REDIS_DATABASE:0}
timeout: ${REDIS_TIMEOUT:2000}ms
lettuce:
pool:
max-active: ${REDIS_POOL_MAX_TOTAL:20}
max-idle: ${REDIS_POOL_MAX_IDLE:10}
min-idle: ${REDIS_POOL_MIN_IDLE:5}
max-wait: -1ms
這個 config 類是為 Redis Client 在 SpringBoot 專案內使用方式做定義
@Configuration
public class RedisConfig {
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory connectionFactory) {
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
redisTemplate.setConnectionFactory(connectionFactory);
Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<>(Object.class);
ObjectMapper om = new ObjectMapper();
om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
om.activateDefaultTyping(LaissezFaireSubTypeValidator.instance, ObjectMapper.DefaultTyping.NON_FINAL);
jackson2JsonRedisSerializer.setObjectMapper(om);
StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
redisTemplate.setKeySerializer(stringRedisSerializer);
redisTemplate.setHashKeySerializer(stringRedisSerializer);
redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);
redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer);
redisTemplate.afterPropertiesSet();
return redisTemplate;
}
}
這是為了簡單測試做的類別,用途是訪問 Redis Server,有點像是用來操作 Redis 的工具類的概念:
@Component
@RequiredArgsConstructor
public class RedisHelper {
private final RedisTemplate<String, Object> redisTemplate;
public Long increment(String cacheName, Object... keys) {
return redisTemplate.opsForValue().increment(cacheName.concat(":").concat(String.valueOf(keys)));
}
}
接著到 Controller 調用看看,然後實際發個 api call 測試有沒有把資料寫進 Redis,並且返回 Redis 裡的資料:
@GetMapping(value = "/redis")
public ResponseEntity<Long> getRedisCount() {
return ResponseEntity.ok(redisHelper.increment(RedisKey.RATE_LIMITER.getValue(), RequestContextHelper.getClientIp()));
}
測試成功!調用後確認 Redis 裡資料被寫入了,只是 key 之後還要細調,但這次就只是單純做個類似 PoC 的動作:
最近幾天工作上有點繁忙,文章的質量稍嫌下降,今天也是在最後關頭,趕快在一個多小時內生出這篇文章,壓線維持住當前的紀錄,還好明天就週末了,一定要在週末加把勁,多做多寫一點,加油!